/*  SSJumpMapView.m
 *  MESLib
 * 
 * Copyright 2003-2009 Starline Software Ltd
 * 
 * This file is part of MESLib.
 * 
 * MESLib is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * MESLib is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with MESLib. If not, see <http://www.gnu.org/licenses/>.
 */
//  Created by Tobias Haughton on Thu Apr 17 2003.
//

#import "SSJumpMapView.h"


@implementation SSJumpMapView

- (id)initWithFrame:(NSRect)frame representedView:(NSView *)rep {
   self = [super initWithFrame:frame];
   if (self) {
      jumpMapRepArray = [[NSMutableArray alloc] init];
      [self setRepresentedView:rep];
      [self setRatios];
      navRect = [[SSJumpMapNavRect alloc] initWithFrame:NSMakeRect(0, 0, (NSWidth([representedView visibleRect]) * xRatio), (NSHeight([representedView visibleRect]) * yRatio))];
      [self setNavRecOrigin];
   }
   return self;
}

- (void)drawRect:(NSRect)rect {
   [[NSColor whiteColor] set];
   [NSBezierPath fillRect:[self bounds]];
   [[NSColor blackColor] set];
   [NSBezierPath strokeRect:[self bounds]];

   //draw all of the represented views
   [jumpMapRepArray makeObjectsPerformSelector:@selector(draw)];

   //draw the navRect
   [[NSColor lightGrayColor] set];
   [navRect draw];
}

//***********************************mouse events
- (void)mouseDown:(NSEvent *)theEvent
{
   NSPoint converted = [self convertPoint:[theEvent locationInWindow] fromView:nil];
   if([navRect containsPoint:converted]) {
      //user has clicked on the navRect
      //get the distance mouse click is from the rects origin (used for dragging)
      distanceFromNavRectOrigin.x = converted.x - [navRect frameOrigin].x;
      distanceFromNavRectOrigin.y = converted.y - [navRect frameOrigin].y;
   } else {
      //center the navRect on the mouse down point
      [navRect setFrameOrigin:NSMakePoint((converted.x - ([navRect frameSize].width / 2)), (converted.y - ([navRect frameSize].height / 2)))];
      [self constrainNavRectIfNeeded];

      NSRect navFrame = [navRect frame];
      [representedView scrollRectToVisible:NSMakeRect((NSMinX(navFrame) / xRatio), (NSMinY(navFrame) / yRatio), (NSWidth(navFrame) / xRatio), (NSHeight(navFrame) / yRatio))];

      //get the distance mouse click is from the rects origin (used for dragging)
      distanceFromNavRectOrigin.x = converted.x - [navRect frameOrigin].x;
      distanceFromNavRectOrigin.y = converted.y - [navRect frameOrigin].y;
      
      [self setNeedsDisplay:YES];
   }
}

- (void)mouseDragged:(NSEvent *)theEvent
{
   NSPoint converted = [self convertPoint:[theEvent locationInWindow] fromView:nil];
   NSPoint newOrigin;
   newOrigin.x =  converted.x - distanceFromNavRectOrigin.x;
   newOrigin.y = converted.y - distanceFromNavRectOrigin.y;

   [navRect setFrameOrigin:newOrigin];
   [self constrainNavRectIfNeeded];

   //scroll the representedView
   NSRect navFrame = [navRect frame];
   [representedView scrollRectToVisible:NSMakeRect((NSMinX(navFrame) / xRatio), (NSMinY(navFrame) / yRatio), (NSWidth(navFrame) / xRatio), (NSHeight(navFrame) / yRatio))];

   [self setNeedsDisplay:YES];
}

//***********************************
- (void)setRepresentedView:(NSView *)rep
{
   representedView = rep;
   [jumpMapRepArray removeAllObjects];

   NSNotificationCenter *nc  = [NSNotificationCenter defaultCenter];
   [nc addObserver:self
          selector:@selector(setRatios)
              name:@"NSViewFrameDidChangeNotification"
            object:representedView];
   [nc addObserver:self
          selector:@selector(setRatios)
              name:@"NSViewBoundsDidChangeNotification"
            object:representedView];
   
   if([representedView enclosingScrollView]) {
      //if the representedView view is inside a scroll view we want to know when it is scrolled
      [nc addObserver:self
             selector:@selector(setNavRecOrigin)
                 name:@"NSViewBoundsDidChangeNotification"
               object:[[representedView enclosingScrollView] contentView]];
      [nc addObserver:self
             selector:@selector(setNavRecOrigin)
                 name:@"NSViewFrameDidChangeNotification"
               object:representedView];
   }
}

- (void)addJumpMapRepForView:(NSView *)view
{
   //takes the passed view creates a jumpmap rep for it and adds it to our jumpMapRepArray
   SSJumpMapSubViewRep *newRep = [[[SSJumpMapSubViewRep alloc] initWithRepView:view xRatio:xRatio yRatio:yRatio] autorelease];
   [jumpMapRepArray addObject:newRep];
   [self setNeedsDisplay:YES];
}

- (void)removeJumpMapRepForView:(NSView *)view
{
   NSEnumerator *enumerator = [jumpMapRepArray objectEnumerator];
   id anObject;

   while (anObject = [enumerator nextObject]) {
      if([[anObject representedView] isEqualTo:view]) {
         //this is the rep we want to remove
         [jumpMapRepArray removeObjectIdenticalTo:anObject];
      }
   }
   
   [self setNeedsDisplay:YES];
}

- (void)setRatios
{
   if([self superview]) { //if we don't have a superview then there is no need to update
      NSEnumerator *enumerator = [jumpMapRepArray objectEnumerator];
      id anObject;

      xRatio = (NSWidth([self bounds]) / NSWidth([representedView bounds]));
      yRatio = (NSHeight([self bounds]) / NSHeight([representedView bounds]));
      [navRect setFrameSize:NSMakeSize((NSWidth([representedView visibleRect]) * xRatio), (NSHeight([representedView visibleRect]) * yRatio))];

      //update the ratios for our jump map reps
      while (anObject = [enumerator nextObject]) {
         [anObject updateWithXRatio:xRatio yRatio:yRatio];
      }

      [self setNeedsDisplay:YES];
   }
}

- (void)setNavRecOrigin
{
   if([self superview]) { //if we don't have a superview then there is no need to update
      [navRect setFrameOrigin:NSMakePoint((NSMinX([representedView visibleRect]) * xRatio), (NSMinY([representedView visibleRect]) * yRatio))];
      [self setNeedsDisplay:YES];
   }
}

- (void)constrainNavRectIfNeeded
{
   //prevent the naveRect from leaving our view
   if([navRect frameOrigin].x < 0) {
      [navRect setFrameOrigin:NSMakePoint(0, [navRect frameOrigin].y)];
   }

   if([navRect frameOrigin].y < 0) {
      [navRect setFrameOrigin:NSMakePoint([navRect frameOrigin].x, 0)];
   }

   if(([navRect frameOrigin].x + [navRect frameSize].width) > NSWidth([self frame])) {
      [navRect setFrameOrigin:NSMakePoint((NSWidth([self frame]) - [navRect frameSize].width), [navRect frameOrigin].y)];
   }

   if(([navRect frameOrigin].y + [navRect frameSize].height) > NSHeight([self frame])) {
      [navRect setFrameOrigin:NSMakePoint([navRect frameOrigin].x, (NSHeight([self frame]) - [navRect frameSize].height))];
   }
}

//***********************************
- (BOOL)isFlipped
{
   //changes the coordinate system so 0,0 is in the top left
   return YES;
}

- (BOOL)isOpaque
{
   // State that the view is opaque for optimization purposes.
   return YES;
}
//***********************************

- (void)dealloc
{
   [[NSNotificationCenter defaultCenter] removeObserver:self];
   
   [jumpMapRepArray release];
   [navRect release];
   [super dealloc];
}

@end
